#include <QDebug>
#include <QRegExp>
#include <QFile>
#include <QStringList>
#include <QFileInfo>
#include <QUrl>

#include "argumentparser.h"

ArgumentParser::ArgumentParser(int argc, char *argv[])
{
	// Accepted arguments
	m_settings = new QMap<QString, Settings::Parameter>;

    // Value types of accepted arguments
    m_parameterTypes = new QMap<Settings::Parameter, ParameterType>;

    m_settings->insert("--uid3", Settings::UID3);
    m_parameterTypes->insert(Settings::UID3, ArgumentParser::String);

    m_settings->insert("--fullscreen", Settings::ShowFullScreen);
    m_parameterTypes->insert(Settings::ShowFullScreen, ArgumentParser::Boolean);

	m_settings->insert("--softkeys", Settings::ShowSoftKeys);
    m_parameterTypes->insert(Settings::ShowSoftKeys, ArgumentParser::Boolean);

    m_settings->insert("--pannable", Settings::PanningEnabled);
    m_parameterTypes->insert(Settings::PanningEnabled, ArgumentParser::Boolean);

    m_settings->insert("--selection", Settings::TextSelectionEnabled);
    m_parameterTypes->insert(Settings::TextSelectionEnabled, ArgumentParser::Boolean);

    m_settings->insert("--log", Settings::LogFilePath);
    m_parameterTypes->insert(Settings::LogFilePath, ArgumentParser::ValidFile);

    m_settings->insert("--fullsdk", Settings::FullS60SDK);
    m_parameterTypes->insert(Settings::FullS60SDK, ArgumentParser::Boolean);

    m_settings->insert("--qtenv", Settings::QtEnvBatPath);
    m_parameterTypes->insert(Settings::QtEnvBatPath, ArgumentParser::Boolean);

	// Command options specifically for the CLI
	m_commandOptions = new QMap<QString, ParameterType>;
	m_commandOptions->insert("--mode", ArgumentParser::Option);
	m_commandOptions->insert("--gui", ArgumentParser::Boolean);
    m_commandOptions->insert("--platform", ArgumentParser::Option);

	// List of unrecognized arguments
	m_invalidArguments = new QStringList();

    // Default values
	m_type = Invalid;
    m_target = NO_TARGET;
	m_gui = true;
    m_platform = "symbian";

	// Parse the arguments
	parse(argc, argv);
}

ArgumentParser::~ArgumentParser()
{
	delete m_settings;
	delete m_commandOptions;
	delete m_parameterTypes;
	delete m_invalidArguments;
}

/**
 * Tries to parse the command line arguments to the settings object.
 */
void ArgumentParser::parse(int argc, char *argv[])
{
	QTextStream cout(stdout, QIODevice::WriteOnly);

	for (int i = 1; i < argc; i++)
	{
		QRegExp rx;
		rx.setPattern("^(--[^=]+)=(.+)$");
		if (rx.indexIn(argv[i]) != -1)
		{
			// Check if the argument is one of recognized ones
			if (m_settings->contains(rx.cap(1)))
			{
				checkSetting(rx.cap(1), rx.cap(2));
			}
			else if (m_commandOptions->contains(rx.cap(1)))
			{
				checkOption(rx.cap(1), rx.cap(2));
			}
			else
			{
				m_invalidArguments->append(rx.cap(1));
			}
		}
		else
		{
			// This may be the project file, check that
			if (QFile::exists(argv[i]))
			{
				QFileInfo info(argv[i]);
				if (info.isDir())
				{
					// Generate from directory
					m_type = Directory;
					m_file = argv[i];
				}
				else if (info.isFile())
				{
					// Generate from widget
					m_type = Widget;
					m_file = argv[i];
				}
			}
			else
			{
				QUrl url = QUrl::fromUserInput(argv[i]);
				if (url.isValid())
				{
					// Generate from web
					m_type = URL;
					m_file = argv[i];
				}
				else
				{
					// Don't know what to do with this argument
					m_invalidArguments->append(argv[i]);
				}
			}
		}
	}

	foreach (QString value, *m_invalidArguments)
	{
		cout << "Ignoring invalid argument: " << value << "\n";
	}
}

/**
 * Checks if the given value for the setting is acceptable, and sets
 * a temporary value for Settings object if so.
 */
bool ArgumentParser::checkSetting(QString setting, QString value)
{
	// Check that the value is conformant
	Settings::Parameter key = m_settings->value(setting);
	switch (m_parameterTypes->value(key))
	{
	case ValidDirectory:
		if (QFile::exists(value))
		{
			Settings::setTemporary(key, value);
			return true;
		}
		break;
	case ValidFile:
		if (QFile::exists(value))
		{
			Settings::setTemporary(key, value);
			return true;
		}
		break;
	case Boolean:
		if (value == "true" || value == "false")
		{
			Settings::setTemporary(key, value == "true" ? true : false);
			return true;
		}
		break;
	case String:
		Settings::setTemporary(key, value);
		return true;
	case Option:
	default:
		break;
	}

	// If we haven't returned yet, the value was invalid and we'll return false
	m_invalidArguments->append(setting);
	return false;
}

/**
 * Checks if the given value for the CLI option is acceptable, and sets
 * a local variable that can be checked later.
 */
bool ArgumentParser::checkOption(QString option, QString value)
{
	// Simplistic check as we don't have that many options yet
	if (option == "--mode")
	{
		if (value == "preview")
            m_target = PREVIEW;
		else if (value == "build")
            m_target = BUILD;
		else if (value == "rebuild")
            m_target = REBUILD;
        else
        {
            m_invalidArguments->append(option);
            return false;
        }

		return true;
	}
	else if (option == "--gui")
	{
		m_gui = (value == "true" ? true : false);
		return true;
	}
    else if (option == "--platform")
    {
        if (value == "maemo" || value == "symbian")
        {
            m_platform = value;
            return true;
        }
        else
        {
            m_invalidArguments->append(option);
            return false;
        }
    }
    else
	{
		m_invalidArguments->append(option);
		return false;
	}
}

/**
 * Tells if we should have a silent, non-gui generator
 */
bool ArgumentParser::guiEnabled() const
{
	return m_gui;
}

/**
 * Returns the file that user wants to process.
 */
QString ArgumentParser::getFile() const
{
	return m_file;
}

/**
 * Returns the target platform
 */
QString ArgumentParser::getPlatform() const
{
    return m_platform;
}

/**
 * Returns the type of the given file.
 */
ArgumentParser::FileType ArgumentParser::getFileType() const
{
	return m_type;
}

/**
 * Returns the specified build action.
 */
GeneratorTarget ArgumentParser::getTarget() const
{
    return m_target;
}
